package com.example.generator.service.impl;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import com.example.generator.entity.GenConfig;
import com.example.generator.mapper.GenConfigMapper;
import com.example.generator.service.CodeGeneratorService;
import com.example.generator.utils.FileUtils;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.zip.ZipOutputStream;

@Service
public class CodeGeneratorServiceImpl extends ServiceImpl<GenConfigMapper, GenConfig> implements CodeGeneratorService {

    @Override
    public void generate(GenConfig config) {
        // 使用 FastAutoGenerator 快速配置代码生成器
        FastAutoGenerator.create(
                        "jdbc:mysql://localhost:3306/blog_cloud?useSSL=false&serverTimezone=Asia/Shanghai",
                        "root",
                        "479368"
                )
                // 全局配置提供了对代码生成器整体行为的设置，包括输出目录、作者信息、Kotlin 模式、Swagger 集成、时间类型策略等。
                // 参考 https://baomidou.com/reference/new-code-generator-configuration/#%E5%85%A8%E5%B1%80%E9%85%8D%E7%BD%AE-globalconfig
                .globalConfig(builder -> {
                    builder.author(config.getAuthor())           // 作者
                            .outputDir(System.getProperty("user.dir") + "/src/main/java") // 输出目录
                            // 设置时间类型策略，默认值: DateType.TIME_PACK
                            .dateType(DateType.ONLY_DATE) // 设置时间类型策略
                            // 设置注释日期格式	默认值: yyyy-MM-dd
                            .commentDate("yyyy-MM-dd") // 设置注释日期
                            .disableOpenDir();          // 生成后不打开目录

                    if (config.isSpringdocEnabled()) {
                        // 开启 Springdoc 注解
                        builder.enableSpringdoc();
                    }
                })
                // 包配置用于定义生成代码的包结构，包括父包名、模块名、实体类包名、服务层包名等。
                // 参考 https://baomidou.com/reference/new-code-generator-configuration/#%E5%8C%85%E9%85%8D%E7%BD%AE-packageconfig
                .packageConfig(builder -> {
                    builder.parent("com.example")       // 父包名
                            .moduleName("system")       // 模块名
                            .entity("model.entity")     // 实体类包名
                            .controller("controller")   // Controller 包名
                            .service("service")         // Service 包名
                            .serviceImpl("service.impl")// Service 实现类包名
                            .mapper("mapper")           // Mapper 接口包名
                            .xml("mapper")              // Mapper XML 包名
                            // 设置 mapper.xml 生成路径
                            .pathInfo(Collections.singletonMap(OutputFile.xml, Paths.get(System.getProperty("user.dir")) + "/project/backend/src/main/resources/mapper"));
                })
                // 策略配置是 MyBatis-Plus 代码生成器的核心部分，它允许开发者根据项目需求定制代码生成的规则，包括命名模式、表和字段的过滤、以及各个代码模块的生成策略。
                // 参考 https://baomidou.com/reference/new-code-generator-configuration/#%E7%AD%96%E7%95%A5%E9%85%8D%E7%BD%AE-strategyconfig
                .strategyConfig(builder -> {
                    builder.addInclude("t_user")  // 需生成的表名
                            .addTablePrefix(config.getTablePrefix())       // 过滤表前缀

                            // 实体类策略配置
                            .entityBuilder()
                            // 覆盖已生成文件	默认值: false
                            .enableFileOverride()
                            // 设置父类
                            .superClass("com.example.system.model.entity.base.BaseEntity")
                            // 禁用生成 serialVersionUID 默认 true
                            .disableSerialVersionUID()
                            // 启用 Lombok 默认值: false 默认只有 Getter,Setter,自3.5.10后增加 ToString
                            .enableLombok()
                            // 开启链式模型	默认值: false
                            .enableChainModel()
                            // 开启 Boolean 类型字段移除 is 前缀	默认值: false
                            .enableRemoveIsPrefix()
                            // 开启生成实体时生成字段注解	默认值: false
                            .enableTableFieldAnnotation()
                            // 开启 ActiveRecord 模型	默认值: false
                            .enableActiveRecord()
                            // 乐观锁字段名(数据库字段)
                            .versionColumnName("version")
                            // 逻辑删除字段名(数据库字段)
                            .logicDeleteColumnName("deleted")
                            // 数据库表映射到实体的命名策略	默认下划线转驼峰命名: NamingStrategy.underline_to_camel
                            .naming(NamingStrategy.no_change)
                            // 数据库表字段映射到实体的命名策略	默认为 null，未指定按照 naming 执行
                            .columnNaming(NamingStrategy.underline_to_camel)
                            // 添加父类公共字段
                            .addSuperEntityColumns("id", "created_by", "created_time", "updated_by", "updated_time")
                            // 添加忽略字段
                            .addIgnoreColumns("age")
                            // 添加表字段填充
                            .addTableFills(new Column("create_time", FieldFill.INSERT))
                            .addTableFills(new Column("updateTime", FieldFill.INSERT_UPDATE))
                            // 全局主键类型
                            .idType(IdType.AUTO)
                            // 格式化文件名称
                            .formatFileName("%sEntity")

                            // Controller 策略配置
                            .controllerBuilder()
                            // 覆盖已生成文件	默认值: false
                            .enableFileOverride()
                            // 开启生成@RestController 控制器	默认值: false
                            .enableRestStyle()
                            .formatFileName("%sController")
                            .template("")

                            // Service 策略配置
                            .serviceBuilder()
                            .enableFileOverride() // 覆盖已生成文件
                            .superServiceClass(IService.class)
                            .formatServiceFileName("%sService") // 格式化 service 接口文件名称，将格式设定为 entity名 + Service
                            .serviceTemplate("/templates/java/service.java") // 设置自定义 Service 模板

                            .superServiceImplClass(ServiceImpl.class)
                            .formatServiceImplFileName("%sServiceImp")
                            .serviceImplTemplate("/templates/java/serviceImpl.java")

                            // Mapper 策略配置
                            .mapperBuilder()
                            .enableFileOverride()
                            .superClass(BaseMapper.class)
                            // 开启 @Mapper 注解	默认值: false
                            .mapperAnnotation(Mapper.class)
                            // 格式化 Mapper 文件名称
                            .formatMapperFileName("%sMapper")
                            .mapperTemplate("/templates/java/mapper.java")
                            // 设置 Mapper XML 策略配置
                            .enableBaseResultMap()
                            // 启用 BaseColumnList	默认值: false
                            .enableBaseColumnList()
                            // 格式化 XML 实现类文件名称
                            .formatXmlFileName("%sMapper")
                            .mapperXmlTemplate("/templates/xml/mapper.xml");
                })
                // 通过 injectionConfig 方法，你可以灵活地自定义模板路径和输出位置，从而生成与后端代码对应的前端 Vue 和 TypeScript 数据表格代码
                // 注入配置允许开发者自定义代码生成器的行为，包括在输出文件之前执行的逻辑、自定义配置 Map 对象、自定义配置模板文件等。
                // 参考 https://baomidou.com/reference/new-code-generator-configuration/#%E6%B3%A8%E5%85%A5%E9%85%8D%E7%BD%AE-injectionconfig
                .injectionConfig(builder -> {
                    builder.customFile(
                                    new CustomFile.Builder()
                                            .enableFileOverride()
                                            .packageName("dto") // 包名,自3.5.10开始,可通过在package里面获取自定义包全路径,低版本下无法获取,示例:package.entityDTO
                                            .fileName("%sDTO.java") // 文件名称
                                            .filePath("src/main/java/com/example/generator/entity") // 文件路径
                                            .templatePath("templates/java/dto.java.ftl") //指定生成模板路径
                                            .build()
                            ) // DTO 文件
                            .customFile(
                                    new CustomFile.Builder()
                                            .enableFileOverride()
                                            .packageName("vo")
                                            .fileName("%sVO.java") // 文件名称
                                            .filePath("src/main/java/com/example/generator/entity") // 文件路径
                                            .templatePath("templates/java/vo.java.ftl") //指定生成模板路径
                                            .build()
                            ) // VO 文件
                            .customFile(
                                    new CustomFile.Builder()
                                            .enableFileOverride()
                                            .packageName("api")
                                            .fileName("api.ts") // 文件名称
                                            .filePath("src/main/java/com/example/generator/entity") // 文件路径
                                            .templatePath("templates/ts/api.ts.ftl") //指定生成模板路径
                                            .build()
                            ) // API 文件
                            .customFile(
                                    new CustomFile.Builder()
                                            .enableFileOverride()
                                            .packageName("types")
                                            .fileName("types.ts") // 文件名称
                                            .filePath("src/main/java/com/example/generator/entity") // 文件路径
                                            .templatePath("templates/ts/types.ts.ftl") //指定生成模板路径
                                            .build()
                            ) // TypeScript 类型定义文件
                            .customFile(
                                    new CustomFile.Builder()
                                            .enableFileOverride()
                                            .packageName("views")
                                            .fileName("table.vue.ftl") // 文件名称
                                            .filePath("src/main/java/com/example/generator/entity") // 文件路径
                                            .templatePath("templates/vue/table.vue.ftl") //指定生成模板路径
                                            .build()
                            ); // Vue 组件文件
                })
                // 模板引擎配置
                .templateEngine(new FreemarkerTemplateEngine())// 使用 Freemarker 引擎模板，默认的是 Velocity 引擎模板
                .execute(); // 执行生成
    }

    @Override
    public File packageToZip(String sourceDir) throws IOException {
        String zipPath = sourceDir + ".zip";
        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath)); FileInputStream fis = new FileInputStream(sourceDir)) {

            FileUtils.zipDirectory(new File(sourceDir), zos, "");
            return new File(zipPath);
        }
    }

    // 定时任务，清理临时文件夹中的文件
    @Scheduled(cron = "0 0 3 * * ?")
    public void cleanTempFiles() {
        FileUtils.deleteDirectory(new File("/tmp/gencode"));
    }
}
